using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Threading;
using Cz;
using Microsoft.Data.Sqlite;

namespace Cz.CzDbSqlite
{
    public class CzDbSqlite : ICzDb
    {
        [CzServiceField] private ICzConfig config;
        [CzServiceField] private ICzLog log;

        private string con;
        private bool debug;
        
        private SqliteConnection dbConnection;
        
        public void Start()
        {
            con = config.Get<string>("sqlite", "con");
            ReLoadConfig();
            config.OnChange += ReLoadConfig;
            
            SQLitePCL.Batteries.Init();

            dbConnection = new SqliteConnection (con);
            var sourceFilePath = dbConnection.DataSource;

            if (!File.Exists(sourceFilePath))
            {
                log.Info("CzDbSqlite create db", $"数据库文件不存在,创建数据库文件{sourceFilePath}");
                Directory.CreateDirectory(Path.GetDirectoryName(sourceFilePath));
            }

            dbConnection.Open ();
            
            log.Info("CzDbSqlite start", "con:" + con);
        }

        public void Dispose()
        {
            Close();
            dbConnection?.Dispose();
        }
        
        public void Close()
        {
            log.Info("CzDbMySQL Close");
            dbConnection?.Close();
        }

        public int Exec(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            return sqlCommand.ExecuteNonQuery();
        }

        public string ExecString(string sql, params DbParameter[] parameters)
        {
            var obj = ExecObject(sql, parameters);
            return obj?.ToString();
        }
        
        public int? ExecInt(string sql, params DbParameter[] parameters)
        {
            var obj = ExecObject(sql, parameters);
            return obj == null ? null : (int?)Convert.ToInt32(obj);
        }
        
        public object ExecObject(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            return sqlCommand.ExecuteScalar();
        }
        
        public Hashtable ExecRow(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            var reader = sqlCommand.ExecuteReader();

            Hashtable ret = null;
            if (reader.Read())
            {
                ret = new Hashtable();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    ret[reader.GetName(i)] = reader[i];
                }
            }
            
            reader.Close();
            return ret;
        }
        
        public List<Hashtable> ExecTable(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = CreateCommand(sql, parameters);
            var reader = sqlCommand.ExecuteReader();
            
            var array = new LinkedList<Hashtable>();
            while (reader.Read())
            {
                var ht = new Hashtable();
                for (int i = 0; i < reader.FieldCount; i++)
                {
                    ht[reader.GetName(i)] = reader[i];
                }
                array.AddLast(ht);
            }
            reader.Close();
            
            var ret = new List<Hashtable>(array.Count);
            ret.AddRange(array);
            return ret;
        }
        
        public DbParameter CreatePara(string name, object val)
        {
            return new SqliteParameter(name, val);
        }

        private SqliteCommand CreateCommand(string sql, params DbParameter[] parameters)
        {
            var sqlCommand = dbConnection.CreateCommand();
            sqlCommand.CommandText = sql;
            sqlCommand.Parameters.AddRange(parameters);
            
            if(debug)
                log.Info("sqlite exec", sqlCommand.CommandText);
            
            return sqlCommand;
        }
        
        private void ReLoadConfig()
        {
            debug = config.GetWithDefault(false, "sqlite", "debug");
        }
    }
}